package com.mo.lib.utils.data

import androidx.annotation.IntRange
import java.lang.NumberFormatException
import java.lang.StringBuilder

/**
 * @ author：mo
 * @ data：2022/4/22:10:39
 * @ 功能：中文数字相关
 */


val sCnNums = charArrayOf('一', '二', '三', '四', '五', '六', '七', '八', '九')
val sCnMonths = charArrayOf('正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊')

/**解析单个常规中文数字字符, 如: 零一...十百千万亿     不包含大写中文数字壹到玖以及廿/卅/腊/冬数字      */
fun parseChar(cnChar: Char): Int {
    for (j in sCnNums.indices) {
        if (sCnNums[j] == cnChar) {
            return j + 1
        }
    }
    when (cnChar) {
        '零'  -> return 0
        '十'  -> return 10
        '百'  -> return 100
        '千'  -> return 1000
        '万'  -> return 10000
        '亿'  -> return 100000000
        else -> {
        }
    }
    throw NumberFormatException(String.format("无法解析字符 \"%s\"", cnChar))
}

/**
 * 将 0 - 10 的数字格式化为中文数字字符
 *
 * @param number 0 - 10 的数字
 */
fun formatChar(number: Int): Char {
    if (number < 0 || number > 10) {
        throw NumberFormatException("不接收 0 - 10 以外的数字")
    }
    if (number == 0) {
        return '零'
    }
    return if (number == 10) {
        '十'
    } else sCnNums[number - 1]
}

/**
 * 解析不带单位的中文数字, 如: 一九九二, 二零零二, 二〇〇二
 *
 * @param cnNumber 不带单位的中文数字
 * @return int 数值
 */
fun parseNumberWithoutUnit(cnNumber: String): Int {
    var result = 0
    outside@ for (i in 0 until cnNumber.length) {
        val cnChar = cnNumber[i]
        for (j in sCnNums!!.indices) {
            if (sCnNums[j] == cnChar) {
                result = result * 10 + (j + 1)
                continue@outside
            }
        }
        if ('零' == cnChar || '〇' == cnChar) {
            result *= 10
            continue
        }
        throw NumberFormatException(String.format("无法解析字符 \"%s\"", cnChar))
    }
    return result
}

/**
 * 格式化数字为不带单位的中文数字, 如: 二零二三四五
 *
 * @param number long 数值
 * @return 不带单位的中文数字表示
 */
fun formatNumberWithoutUnit(number: Long): String? {
    var number = number
    val builder = StringBuilder()
    while (number > 0) {
        builder.insert(0, formatChar((number % 10).toInt()))
        number /= 10
    }
    return builder.toString()
}

/**
 * 解析中文数字, 如: 一万八千零八(18008), 一百八(180), 十八(18)
 *
 * @param cnNumber 中文数字, 一亿以内
 * @return int 数值
 */
fun parseNumber(cnNumber: String): Int {
    val result = IntArray(5)
    var unit = 1
    result[0] = 1
    val zero = '零'
    val wan = '万'
    val units = charArrayOf('十', '百', '千')
    val figures = intArrayOf(1, 10, 100, 1000, 10000)
    var methods = 0x1111
    outside@ for (i in 0 until cnNumber.length) {
        val cnChar = cnNumber[i]

        // 一到九
        if (methods and 0x0001 == 0x0001) {
            for (j in sCnNums!!.indices) {
                if (sCnNums[j] == cnChar) {
                    result[0] = j + 1
                    methods = 0x0110
                    continue@outside
                }
            }
        } // 十百千
        if (methods and 0x0010 == 0x0010) {
            for (j in units.indices) {
                if (units[j] == cnChar) {
                    unit = j + 1
                    result[unit] += result[0]
                    result[0] = 0
                    methods = 0x1101
                    continue@outside
                }
            }
        } // 万
        if (methods and 0x0100 == 0x0100) {
            if (wan == cnChar) {
                unit = 4
                result[unit] = result[unit] * figures[unit]
                for (k in 0 until unit) {
                    result[unit] += result[k] * figures[k]
                    result[k] = 0
                }
                methods = 0x1101
                continue
            }
        } // 零
        if (methods and 0x1000 == 0x1000 && zero == cnChar) {
            result[0] = 0
            methods = 0x1001
            continue
        }
        val error = if (i > 0) String.format("无法解析字符 \"%s\" 或组合 \"%s\"", cnChar, cnNumber.substring(i - 1, i + 1)) else String.format("无法解析字符 \"%s\"", cnChar)
        throw NumberFormatException(error)
    }
    return result[4] * figures[4] + result[3] * figures[3] + result[2] * figures[2] + result[1] * figures[1] + result[0] * figures[unit] / 10
}

/**
 * 解析中文数字, 如: 一万八千零八(18008), 一百八(180), 十八(18)
 *
 * @param cnNumber 中文数字, 接受一亿及以上的大数字
 * @return long 数值
 */
fun parseNumberAsLong(cnNumber: String): Long {
    val result = LongArray(6)
    var unit = 1
    result[0] = 1
    val zero = '零'
    val units = charArrayOf('十', '百', '千')
    val bigUnits = charArrayOf('万', '亿')
    val figures = longArrayOf(1L, 10L, 100L, 1000L, 10000L, 100000000L)
    var methods = 0x1111
    outside@ for (i in 0 until cnNumber.length) {
        val cnChar = cnNumber[i]

        // 一到九
        if (methods and 0x0001 == 0x0001) {
            for (j in sCnNums!!.indices) {
                if (sCnNums[j] == cnChar) {
                    result[0] = (j + 1).toLong()
                    methods = 0x0110
                    continue@outside
                }
            }
        } // 十百千
        if (methods and 0x0010 == 0x0010) {
            for (j in units.indices) {
                if (units[j] == cnChar) {
                    unit = j + 1
                    result[unit] += result[0]
                    result[0] = 0
                    methods = 0x1101
                    continue@outside
                }
            }
        } // 万亿
        if (methods and 0x0100 == 0x0100) {
            for (j in bigUnits.indices) {
                if (bigUnits[j] == cnChar) {
                    unit = j + 4
                    result[unit] = result[unit] * figures[unit]
                    for (k in 0 until unit) {
                        result[unit] += result[k] * figures[k]
                        result[k] = 0
                    }
                    methods = 0x1101
                    continue@outside
                }
            }
        } // 零
        if (methods and 0x1000 == 0x1000 && zero == cnChar) {
            result[0] = 0
            methods = 0x1001
            continue
        }
        val error = if (i > 0) String.format("无法解析字符 \"%s\" 或组合 \"%s\"", cnChar, cnNumber.substring(i - 1, i + 1)) else String.format("无法解析字符 \"%s\"", cnChar)
        throw NumberFormatException(error)
    }
    return result[5] * figures[5] + result[4] * figures[4] + result[3] * figures[3] + result[2] * figures[2] + result[1] * figures[1] + result[0] * figures[unit] / 10
}

/**
 * 解析农历年份, 例: 一九九二(1992), 九二年(1992), 零八(2008), 一七年(2017), 二零(1920)
 *
 * @param lunarYear 农历年份, 简写时默认为过去的年份
 * @return int 数值
 */
fun parseLunarYear(lunarYear: String): Int {
    var year = lunarYear
    if (year[year.length - 1] == '年') {
        year = year.substring(0, year.length - 1)
    }
    if (year.length > 4 || year.length == 0) {
        throw NumberFormatException("无法解析输入的年份 \"$lunarYear\"")
    }
    var result = parseNumberWithoutUnit(year)
    if (year.length <= 2) {
        result += if (result <= 17) {
            2000
        } else {
            1900
        }
    }
    return result
}

/**
 * 解析农历月份, 例: 一月(1), 正月(1), 二(2), 冬(11), 腊月(12)等
 *
 * @param lunarMonth 农历月份, 末尾可带可不带 '月' 字
 * @return int 数值
 */
fun parseLunarMonth(lunarMonth: String): Int {
    var lunarMonth = lunarMonth
    if (lunarMonth[lunarMonth.length - 1] == '月') {
        lunarMonth = lunarMonth.substring(0, lunarMonth.length - 1)
    }
    when (lunarMonth) {
        "一", "正"  -> return 1
        "二"       -> return 2
        "三"       -> return 3
        "四"       -> return 4
        "五"       -> return 5
        "六"       -> return 6
        "七"       -> return 7
        "八"       -> return 8
        "九"       -> return 9
        "十"       -> return 10
        "十一", "冬" -> return 11
        "十二", "腊" -> return 12
        else      -> {
        }
    }
    return 0
}

/**
 * 格式化数字为农历月份
 *
 * @param month 1 - 12 代表月份
 * @return 农历月份字符
 */
fun formatLunarMonth(month: Int): Char {
    val index = month % 12 - 1
    return if (index >= 0) {
        sCnMonths[index]
    } else sCnMonths[12 + index]
}

/**
 * 解析农历日期中的日, 如: 初一(1), 一(1), 十三(13), 廿二(22), 二十二(22), 卅(30), 三十(30)
 *
 * @param lunarDay 农历日期中的日, 末尾可带可不带 '日' 字
 * @return int 数值
 */
fun parseLunarDay(lunarDay: String): Int {
    var lunarDay = lunarDay
    if (lunarDay[lunarDay.length - 1] == '日') {
        lunarDay = lunarDay.substring(0, lunarDay.length - 1)
    }
    var result = 0
    var temp = 0
    var methods = 0x111
    outside@ for (i in 0 until lunarDay.length) {
        val lunarChar = lunarDay[i]
        if (methods and 0x001 == 0x001) {
            if ('初' == lunarChar) {
                methods = 0x110
                continue
            }
            if ('廿' == lunarChar) {
                result = 20
                methods = 0x100
                continue
            }
            if ('卅' == lunarChar) {
                result = 30
                methods = 0x100
                continue
            }
        }
        if (methods and 0x010 == 0x010 && '十' == lunarChar) {
            result = if (temp == 0) 10 else temp * 10
            temp = 0
            methods = 0x100
            continue
        }
        if (methods and 0x100 == 0x100) {
            for (j in sCnNums!!.indices) {
                if (sCnNums[j] == lunarChar) {
                    temp = j + 1
                    methods = 0x010
                    continue@outside
                }
            }
        }
        val error = if (i > 0) String.format("无法解析字符 \"%s\" 或组合 \"%s\"", lunarChar, lunarDay.substring(i - 1, i + 1)) else String.format("无法解析字符 \"%s\"", lunarChar)
        throw NumberFormatException(error)
    }
    return result + temp
}

/**
 * 格式化数字为农历日份, 如 1 -> 初一、 20 -> 二十
 *
 * @param day 农历日份数值
 * @return 字符串表示的农历日份
 */
fun formatLunarDay(@IntRange(from = 1, to = 31) day: Int): String? {
    require(!(day < 1 || day > 31)) { "无法接收 1 - 31 以外的数值" }
    return if (day == 10) {
        "初十"
    } else if (day == 20) {
        "二十"
    } else if (day == 30) {
        "三十"
    } else {
        val cnFigures = charArrayOf('初', '十', '廿', '卅')
        cnFigures[day / 10].toString() + sCnNums!![day % 10 - 1].toString()
    }
}